home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SPACE 2
/
SPACE - Library 2 - Volume 1.iso
/
magazi~1
/
278
/
vdigrph.9
< prev
next >
Wrap
Text File
|
1986-11-01
|
25KB
|
526 lines
Permission to reprint or excerpt is granted only if the following
line appears at the top of the article:
ANTIC PUBLISHING INC., COPYRIGHT 1986. REPRINTED BY PERMISSION.
PROFESSIONAL GEM by Tim Oren
Column #9 - VDI Graphics: Lines and Solids
This issue of ST PRO GEM is the first in a series of two which
will explore the fundamentals of VDI graphics output. In this
installment, we will take a look at the commands necessary to
output simple graphics such as lines, squares and circles as well
as more complex figures such as polygons. The following episode
will take a first look at graphics text output, with an emphasis
on ways to optimize its drawing speed. It will also include
another installment of ONLINE Feedback. As usual, there is a
download with this column. You should find it under the name
GEMCL9.C in DL3 of ATARI16 (PCS-58).
A BIT OF HISTORY. One of the reasons that the VDI can be
confusing is that drawing anything at all, even a simple line, can
involve setting around four different VDI parameters before making
the draw call! (Given the state of the GEM documents, just FINDING
them can be fun!) Looking backwards a bit sheds some light on why
the VDI is structured this way, and also gives us a framework for
organizing a discussion of graphics output.
The GEM VDI closely follows the so-called GKS standard, which
defines capabilities and calling sequences for a standardized
graphic input/output system. GKS is itself an evolution from an
early system called "Core". Both of these standards were born in
the days when pen plotters, vectored graphics displays, and
minicomputers were the latest items. So, if you wonder why
setting the drawing pen color is a separate command, just think
back a few years when it actually meant what it says! (The
cynical may choose instead to ponder the benefits of
standardization.)
When doing VDI output, it helps if you pretend that the
display screen really is a plotter or some other separate device,
which has its own internal parameters which you can set up and
read back. The class of VDI commands called Attribute Functions
let you set the parameters. Output Functions cause the "device"
to actually draw someone once it is configured. The Inquire
Functions let you read back the parameters if necessary.
There are two parameters which are relevant no matter what
type of object you are trying to draw. They are the writing mode
and the clipping rectangle. The writing mode is similar to that
discussed in the column on raster operations. It determines what
effect the figure you are drawing will have on data already on the
screen. The writing mode is set with the call:
vswr_mode(vdi_handle, mode);
Vdi_handle, here and below, is the handle obtained from
graf_handle at the beginning of the program. Mode is a word which
may be one of:
1 - Replace Mode,
2 - Transparent Mode,
3 - XOR mode,
4 - Reverse Transparent Mode.
In replace mode, whatever is on the screen is overwritten.
If you are writing characters, this means the background of each
character cell will be erased.
In transparent mode, only the pixels directly under the
"positive" part of the image, that is, where 1-bits are being
written, will be changed. When writing characters, the background
of the cell will be left intact.
In XOR mode, an exclusive or is performed between the screen
contents and what is being written. The effect is to reverse the
image under areas where a 1-bit occurs.
Reverse transparent is like transparent, but with a "reverse
color scheme". That is, only places where a 0-bit is to be
put are changed to the current writing color. When you
write characters in reverse transparent (over white), the effect
is reverse video.
The other common parameter is the clipping rectangle. It
defines the area on the screen where the VDI is permitted to draw.
Any output which would fall outside of this area is ignored; it is
effectively a null operation. The clip rectangle is set with the
call:
vs_clip(vdi_handle, flag, pxy);
Pxy is a four-word array. Pxy[0] and pxy[1] are the X and Y
screen coordinates, respectively, of one corner of your clipping
rectangle. Pxy[2] and pxy[3] are the coordinates of the
diagonally opposite corner of the rectangle. (When working with
the AES, use of a GRECT to define the clip is often more
convenient. The routine set_clip() in the download does this.)
Flag is set to TRUE if clipping is to be used. If you set it
to FALSE, the entire screen is assumed to be fair game.
Normally, you should walk the rectangle list for the current
window to obtain your clipping rectangles. (See ST PRO GEM #2 for
more details.) However, turning off the clip speeds up all output
operations, particularly text. You may do this ONLY when you are
absolutely certain that the figure you are drawing will not extend
out of the top-most window, or out of a dialog.
THE LINE FORMS ON THE LEFT. The VDI line drawing operations
include polyline, arc, elliptical arc, and rounded rectangle.
I'll first look at the Attribute Functions for line drawing, then
go through the drawing primitives themselves.
The most common used line attributes are color and width.
The color is set with:
vsl_color(vdi_handle, color);
where color is one of the standard VDI color indices, ranging
from zero to 15. (As discussed in column #6, the color which
actually appears will depend on the pallette setting of your ST.)
The line width may only be set to ODD positive values, for
reasons of symmetry. If you try to set an even value, the VDI
will take the next lower odd value. The call is:
vsl_width(vdi_handle, width);
The two less used line parameters are the end style and
pattern. With the end style you can cause the output line to
have rounded ends or arrowhead ends. The call is:
vsl_ends(vdi_handle, begin_style, end_style);
Begin_style and end_style are each words which may have the
values zero for square ends (the default), one for arrowed ends,
or two for rounded ends. They determine the styles for the
starting and finishing ends of the line, respectively.
The line pattern attribute can select dotted or dashed lines
as well as more complicated patterns. Before continuing, you
should note one warning: VDI line output DOES NOT compensate for
pixel aspect ratio. That is, the dashes on a line will look twice
as long drawn vertically on a medium-res ST screen as they do when
drawn horizontally. The command for setting the pattern is:
vsl_type(vdi_handle, style);
Style is a word with a value between 1 and 7. The styles
selected are:
1 - Solid (the default)
2 - Long Dash
3 - Dot
4 - Dash, Dot
5 - Dash
6 - Dash, Dot, Dot
7 - (User defined style)
The user defined style is determined by a 16-bit pattern
supplied by the application. A one bit in the pattern turns a
pixel on, a zero bit leaves it off. The pattern is cycled through
repeatedly, using the high bit first. To use a custom style, you
must make the call:
vsl_udsty(vdi_handle, pattern);
before doing vsl_type().
As I mentioned above, the line type Output Functions
available are polyline, circular and ellliptical arc, and rounded
rectangle. Each has its own calling sequence. The call for a
polyline is:
v_pline(vdi_handle, points, pxy);
Points tells how many vertices will appear on the polyline. For
instance, a straight line has two vertices: the end and the
beginning. A closed square would have five, with the first and
last identical. (There is no requirement that the figure
described be closed.)
The pxy array contains the X and Y raster coordinates for the
vertices, with a total of 2 * points entries. Pxy[0] and pxy[1]
are the first X-Y pair, and so on.
If you happen to be using the XOR drawing mode, remember that
drawing twice at a point is equivalent to no drawing at all.
Therefore, for a figure to appear closed in XOR mode, the final
stroke should actually stop one pixel short of the origin of the
figure.
You may notice that in the GEM VDI manual the rounded
rectangle and arc commands are referred to as GDPs (Generalized
Drawing Primitives). This denotation is historical in nature, and
has no effect unless you are writing your own VDI bindings.
The rounded rectangle is nice to use for customized buttons
in windows and dialogs. It gives a "softer" look to the screen
than the standard square objects. The drawing command is:
v_rbox(vdi_handle, pxy);
Pxy is a four word array giving opposite corners of the
rectangle, just as for the vs_clip() call. The corner rounding
occurs within the confines of this rectangle. Nothing will
protrude unless you specify a line thickness greater than one.
The corner rounding is approximately circular; there is no user
control over the degree or shape of rounding.
Both the arc and elliptical arc commands use a curious method
of specifying angles. The units are tenths of degrees, so an
entire circle is 3600 units. The count starts at ninety degrees
right of vertical, and proceeds counterclockwise. This means that
"3 o'clock" is 0 units, "noon" is 900 units, "9 o'clock" is 1800
units, and 2700 units is at "half-past". 3600 units take you back
to "3 o'clock".
The command for drawing a circular arc is:
v_arc(vdi_handle, x, y, radius, begin, end);
X and y specify the raster coordinates of the center of the
circle. Radius specifies the distance from center to all points
on the arc. Begin and end are angles given in units as described
above, both with values between 0 and 3600. The drawing of the
arc ALWAYS proceeds counterclockwise, in the direction of
increasing arc number. So values of 0 and 900 for begin and end
would draw a quarter circle from "three o'clock" to "noon".
Reversing the values would draw the other three quarters of the
circle.
A v_arc() command which specifies a "full turn" is the
fastest way to draw a complete circle on the screen. Be warned,
however, that the circle drawing algorithm used in the VDI seems
to have some serious shortcomings at small radii! You can
experiment with the CIRCLE primitive in ST Logo, which uses
v_arc(), to see what I mean.
Notice that if you want an arc to strike one or more given
points on the screen, then you are in for some trigonometry. If
your math is a bit rusty, I highly recommend the book "A
Programmer's Geometry", by Bowyer and Woodwark, published by
Butterworths (London, Boston, Toronto).
Finally, the elliptical arc is generated with:
v_ellarc(vdi_handle, x, y, xrad, yrad, begin, end);
X, y, begin, and end are just as before. Xrad and yrad give the
horizontal and vertical radii of the defining ellipse. This means
that the distance of the arc from center will be yrad pixels at
"noon" and "half-past", and it will be xrad pixels at "3 and 9
o'clock". Again, the arc is always drawn counterclockwise.
There are a number of approaches to keeping the VDI's
attributes "in sync" with the actual output operations. Probably
the LEAST efficient is to use the Inquire Functions to determine
the current attributes. For this reason, I have omitted a
discussion of these calls from this column.
Another idea is to keep a local copy of all significant
attributes, use a test-before-set method to minimize the number of
Attribute Functions which need to be called. This puts a burden
on the programmer to be sure that the local attribute variables
are correctly maintained. Failure to do so may result in obscure
drawing bugs. If your application employs user defined AES
objects, you must be very careful because GEM might call your draw
code in the middle of a VDI operation (particularly if the user
defined objects are in the menu).
Always setting the attributes is a simplistic method, but
often proves most effective. The routines pl_perim() and
rr_perim() in the download exhibit this approach. Modification
for other primitives is straightforward. This style is most
useful when drawing operations are scattered throughout the
program, so that keeping track of the current attribute status is
difficult. Although inherently inefficient, the difference is not
very noticable if the drawing operation requested is itself time
consuming.
In many applications, such as data graphing programs or
"Draw" packages, the output operations are centralized, forming
the primary functionality of the code. In this case, it is both
easy and efficient to keep track of attribute status between
successive drawing operations.
SOLIDS. There are a wider variety of VDI calls for drawing
solid figures. They include rectangle or bar, disk, pie, ellipse,
elliptical pie, filled rounded rectangle, and filled polygonal
area. Of course, filled figure calls also have their own set of
attributes which you will need to set.
The fill color index determines what pen color will be used
to draw the solid. It is set with:
vsf_color(vdi_handle, color);
Color is just the same as for line drawing. A solid may or
may not have a visible border. This is determined with the call:
vsf_perimeter(vdi_handle, vis);
Vis is a Boolean. If it is true, the figure will be given a
solid one pixel outline in the current fill color index. This is
often useful to improve the appearance of solids drawn with a
dithered fill pattern. If vis is false, then no outline is drawn.
There are two parameters which together determine the pattern
used to fill your figure. They are called interior style and
interior index. The style determines the general type of fill,
and the index is used to select a particular pattern if necessary.
The style is set with the command:
vsf_interior(vdi_handle, style);
where style is a value from zero through four. Zero selects a
hollow style: the fill is performed in color zero, which is
usually white. Style one selects a solid fill with the current
fill color. A style of two is called "pattern" and a three is
called "hatch", which are terms somewhat suggestive of the options
which can then be selected using the interior index. Style four
selects the user defined pattern, which is described below.
The interior index is only significant for styles two and
three. To set it, use:
vsf_style(vdi_handle, index);
(Be careful here: it is very easy to confuse this call with the
one above due to the unfortunate choice of name.) The index
selects the actual drawing pattern. The GEM VDI manual shows fill
patterns corresponding to index values from 1 to 24 under style 2,
and from 1 to 12 under style 3. However, some of these are
implemented differently on the ST. Rather than try to describe
them all here, I would suggest that you experiment. You can do so
easily in ST Logo by opening the Graphics Settings dialog and
playing with the style and index values there.
The user defined style gives you some interesting options for
multi-color fills. It is set with:
vsf_udpat(vdi_handle, pattern, planes);
Planes determines the number of color planes in the pattern
which you supply. It is set to one if you are setting a
monochrome pattern. (Remember, monochrome is not necessarily
black). It may be set to higher values on color systems: two for
ST medium-res mode, or four for low-res mode. If you use a number
lower than four under low-res, the other planes are zero filled.
The pattern parameter is an array of words which is a
multiple of 16 words long. The pattern determined is 16 by 16
pixels, with each word forming one row of the pattern. The rows
are arranged top to bottom, with the most significant bit to the
left. If you have selected a multi-plane pattern, the entire
first plane is stored, then the second, and so on.
Note that to use a multi-plane pattern, you set the writing
mode to replace using vswr_mode(). Since the each plane can be
different, you can produce multi-colored patterns. If you use a
writing color other than black, some of the planes may
"disappear".
Most of the solids Output Functions have analogous line
drawing commands. The polyline command corresponds to the filled
area primitive. The filled area routine is:
v_fillarea(vdi_handle, count, pxy);
Count and pxy are just the same as for v_pline(). If the
polygon defined by pxy is not closed, then the VDI will force
closure with a straight line from the last to the first point.
The polygon may be concave or self-intersecting. If perimeter
show is on, the area will be outlined.
One note of caution is necessary for both v_fillarea() and
v_pline(). There is a limit on the number of points which may be
stored in pxy[]. This limit occurs because the contents of pxy[]
are copied to the intin[] binding array before the VDI is called.
You can determine the maximum number of vertices by checking
intout[14] after using the extended inquire function vq_extnd().
For reasons unknown to this writer, there are TWO different
filled rectangle commands in the VDI. The first is
vr_recfl(vdi_handle, pxy);
Pxy is a four word array defining two opposite corners of the
rectangle, just as in vs_clip(). Vr_recfl() uses the fill
attribute settings, except that it NEVER draws a perimeter.
The other rectangle routine is v_bar(), with exactly the same
arguments as vr_recfl(). The only difference is that the
perimeter setting IS respected. These two routines are the
fastest way to produce a solid rectangle using the VDI. They may
be used in XOR mode with a BLACK fill color to quickly invert an
area of the screen. You can improve the speed even further by
turning off the clip (if possible), and byte aligning the left and
right edges of the rectangle.
Separate commands are provided for solid circle and ellipse.
The circle call is:
v_circle(vdi_handle, x, y, radius);
and the ellipse command is:
v_ellipse(vdi_handle, x, y, xrad, yrad);
All of the parameters are identical to those given above for
v_arc() and v_ellarc(). The solid analogue of an arc is a "pie
slice". The VDI pie commands are:
v_pieslice(vdi_handle, x, y, radius, begin, end);
for a slice from a circular pie, and
v_ellpie(vdi_handle, x, y, xrad, yrad, begin, end);
for a slice from a "squashed" pie. Again, the parameters are
identical to those in v_arc() and v_ellarc(). The units and
drawing order of angles are also the same. The final solids
Output Function is:
v_rfbox(vdi_handle, pxy);
which draws a filled rounded rectangle. The pxy array defines
two opposite corners of the bounding box, as shown for vs_clip().
The issues involved in correctly setting the VDI attributes
for a fill operation are identical to those in drawing lines. For
those who want to employ the "always set" method, I have again
included two skeleton routines in the download, which can be
modified as desired.
TO BE CONTINUED. This concludes the first part of our
expedition through basic VDI operations. The next issue will
tackle the problems of drawing bit mapped text at a reasonable
speed. This first pass will not attempt to tackle alternate or
proportional fonts, or alternate font sizes. Instead, I will
concentrate on techniques for squeezing greater performance out of
the standard monospaced system fonts.
>>>>>>>>>>>>>>>> Routines to set clip to a GRECT <<<<<<<<<<<<<<<<
VOID
grect_to_array(area, array) /* convert x,y,w,h to upr lt x,y and */
GRECT *area; /* lwr rt x,y */
WORD *array;
{
*array++ = area->g_x;
*array++ = area->g_y;
*array++ = area->g_x + area->g_w - 1;
*array = area->g_y + area->g_h - 1;
}
VOID
set_clip(clip_flag, s_area) /* set clip to specified area */
WORD clip_flag;
GRECT *s_area;
{
WORD pxy[4];
grect_to_array(s_area, pxy);
vs_clip(vdi_handle, clip_flag, pxy);
}
>>>>>>>>>> Routines to set attributes before output <<<<<<<<<<<<
VOID
rr_perim(mode, color, type, width, pxy) /* Draw a rounded */
WORD mode, color, width, *pxy; /* rectangle outline */
{
vswr_mode(vdi_handle, mode);
vsl_color(vdi_handle, color);
vsl_type(vdi_handle, type);
vsl_width(vdi_handle, width);
v_rbox(vdi_handle, pxy);
vswr_mode(vdi_handle, MD_REPLACE);
}
VOID
pl_perim(mode, type, color, width, npts, pxy) /* Draw a polygonal */
/* figure */
WORD mode, type, color, width, npts, *pxy;
{
vswr_mode(vdi_handle, mode);
vsl_type(vdi_handle, type);
vsl_color(vdi_handle, color);
vsl_width(vdi_handle, width);
v_pline(vdi_handle, npts, pxy);
}
VOID /* Draw a filled polygonal area */
pl_fill(mode, perim, color, interior, style, npts, pxy)
WORD mode, perim, color, interior, style, npts, *pxy;
{
vswr_mode(vdi_handle, mode);
vsf_color(vdi_handle, color);
vsf_style(vdi_handle, style);
vsf_interior(vdi_handle, interior);
vsf_perimeter(vdi_handle, perim);
v_fillarea(vdi_handle, npts, pxy);
}
VOID /* Draw a filled rectangle */
rect_fill(mode, perim, color, interior, style, pxy)
WORD mode, perim, color, style, interior, *pxy;
{
vswr_mode(vdi_handle, mode);
vsf_color(vdi_handle, color);
vsf_style(vdi_handle, style);
vsf_interior(vdi_handle, interior);
vsf_perimeter(vdi_handle, perim);
vr_recfl(vdi_handle, pxy);
}